package org.simp.node;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.simp.codec.SimpMessageCodecFactory;
import org.simp.common.Constants;
import org.simp.context.SimpContext;
import org.simp.context.SimpContextWraper;
import org.simp.core.message.SimpMessage;
import org.simp.handler.AbstractSimpHandler;
import org.simp.interceptor.SimpHandlerInterceptor;
import org.simp.router.RouterInterceptor;

public class SimpClientConnector {

private static final Logger log = Logger.getLogger(SimpClientConnector.class);
	
	private SimpContext context;
	
	private String ip;
	
	private int port;
	
	private AbstractSimpHandler simpHanlder ;
	
	private SimpHandlerInterceptor handlerInterceptor;
	
	private RouterInterceptor routerInterceptor;
	
	private ProtocolCodecFilter[] codecFilters;

	private boolean isConnected ;
	/**
	 * 自动重连，默认为true
	 */
	private boolean autoReConnecting = true;
	
	private NioSocketConnector connector;
	
	private SimpNode node;
	
	public SimpClientConnector(String ip,int port,String serverName,String name,SimpContext context,AbstractSimpHandler handler,SimpNode node){
		this.ip = ip;
		this.port = port;
		this.context = context;
		this.node = node;
		initContext(serverName,name);
		this.simpHanlder = handler;
	}
	
	public void initContext(String serverName,String name){
		if(context == null)
			context = new SimpContextWraper();
		context.addAttribute(Constants.NODE_SERVER_NAME, serverName);
		context.addAttribute(Constants.NODE_NAME, name);
		context.addAttribute(Constants.NODE, this);
		context.setSimpNode(node);
	}
	
	public void initConnect(){
		connector = new NioSocketConnector();
		connector.getSessionConfig().setKeepAlive(true);
	    connector.getSessionConfig().setSoLinger(0);
	    
	    if (codecFilters != null) {
		      for (int i = 0; i < codecFilters.length; i++) {
		    	  connector.getFilterChain().addLast("customCodec" + i, codecFilters[i]);
		      }
		    }
	    
	    connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new SimpMessageCodecFactory()));

	    ExecutorFilter ef = new ExecutorFilter(new ThreadPoolExecutor(16, 48, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue()));

	    connector.getFilterChain().addLast("threadPool", ef);
	    
	    simpHanlder.init(context, routerInterceptor, handlerInterceptor);
	    connector.setHandler(simpHanlder);
	}
	public void connect(){
			initConnect();
		    ConnectFuture future = connector.connect(getAddress());
		    future.awaitUninterruptibly();
		    if(future.isConnected()){
		    	isConnected = true;
		    	log.info(" connect success");
		    	onConnected();
		    }else {
		    	reConnect();
		    }
	}
	
	public void reConnect(){
		if(autoReConnecting ){
			isConnected = false;
			while(!isConnected){
				log.info(" connect server error, reconnecting ...");
			try {
				Thread.sleep(30000);
				connect();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			}
		}
	}
	/**
	 * client刚连上服务器时调用
	 * 
	 * @author zhuhaitao at 2014-5-26
	 */
	public  void onConnected() {
		// TODO Auto-generated method stub
		
	}

	public void close(){
		connector.dispose(true);
		autoReConnecting = false;
		isConnected = false;
	}
	
	public AbstractSimpHandler getSimpHanlder() {
		return simpHanlder;
	}

	public void setSimpHanlder(AbstractSimpHandler simpHanlder) {
		this.simpHanlder = simpHanlder;
	}

	public SimpHandlerInterceptor getHandlerInterceptor() {
		return handlerInterceptor;
	}

	public void setHandlerInterceptor(SimpHandlerInterceptor handlerInterceptor) {
		this.handlerInterceptor = handlerInterceptor;
	}

	public RouterInterceptor getRouterInterceptor() {
		return routerInterceptor;
	}

	public void setRouterInterceptor(RouterInterceptor routerInterceptor) {
		this.routerInterceptor = routerInterceptor;
	}

	public boolean isConnected() {
		return isConnected;
	}

	public void setConnected(boolean isConnected) {
		this.isConnected = isConnected;
	}

	public boolean isAutoReConnecting() {
		return autoReConnecting;
	}

	public void setAutoReConnecting(boolean autoReConnecting) {
		this.autoReConnecting = autoReConnecting;
	}

	public ProtocolCodecFilter[] getCodecFilters() {
		return codecFilters;
	}

	public void setCodecFilters(ProtocolCodecFilter[] codecFilters) {
		this.codecFilters = codecFilters;
	}

	
	public SimpNode getNode() {
		return node;
	}

	public void setNode(SimpNode node) {
		this.node = node;
	}

	public SocketAddress getAddress(){
		SocketAddress address = new InetSocketAddress(ip,port) ;
		return address;
	}
	
	public void send(SimpMessage message){
		simpHanlder.send(message);
	}
}
